/*
 * Copyright 2009 Phil Burk, Mobileer Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.example;

import com.jsyn.JSyn;
import com.jsyn.Synthesizer;
import com.jsyn.unitgen.LineOut;
import com.jsyn.unitgen.SawtoothOscillator;
import com.jsyn.unitgen.SineOscillator;
import com.softsynth.shared.time.TimeStamp;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static org.example.Main.generateTwelveToneEqualTemperament;

/**
 * Play notes using timestamped noteOn and noteOff methods of the UnitVoice.
 *
 * @author Phil Burk (C) 2009 Mobileer Inc
 */
public class PlayNotes {

    private void test() {

        // Create a context for the synthesizer.
        Synthesizer synth = JSyn.createSynthesizer();
        // Set output latency to 123 msec because this is not an interactive app.
        synth.getAudioDeviceManager().setSuggestedOutputLatency(0.123);

        // Add a tone generator.
        SawtoothOscillator voice = new SawtoothOscillator();
        synth.add(voice);
        // synth.add( ugen = new SineOscillator() );
        // synth.add( ugen = new SubtractiveSynthVoice() );
        // Add an output mixer.
        LineOut lineOut = new LineOut();
        synth.add(lineOut);

        // Connect the oscillator to the left and right audio output.
        voice.getOutput().connect(0, lineOut.input, 0);
        voice.getOutput().connect(0, lineOut.input, 1);

        // Start synthesizer using default stereo output at 44100 Hz.
        synth.start();

        // Get synthesizer time in seconds.
        double timeNow = synth.getCurrentTime();

        // Advance to a near future time so we have a clean start.
        TimeStamp timeStamp = new TimeStamp(timeNow + 0.5);

        // We only need to start the LineOut. It will pull data from the
        // oscillator.
        synth.startUnit(lineOut, timeStamp);

        // Schedule a note on and off.
        double freq = 200.0; // hertz
        double duration = 2;
        double onTime = 1;
        voice.noteOn(freq, 0.5, timeStamp);
        voice.noteOff(timeStamp.makeRelative(onTime));

        // Schedule this to happen a bit later.
        timeStamp = timeStamp.makeRelative(duration);
        freq *= 1.5; // up a perfect fifth
        voice.noteOn(freq, 0.5, timeStamp);
        voice.noteOff(timeStamp.makeRelative(onTime));

        timeStamp = timeStamp.makeRelative(duration);
        freq *= 4.0 / 5.0; // down a major third
        voice.noteOn(freq, 0.5, timeStamp);
        voice.noteOff(timeStamp.makeRelative(onTime));

        // Sleep while the song is being generated in the background thread.
        try {
            synth.sleepUntil(timeStamp.getTime() + 2.0);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // Stop everything.
        synth.stop();
    }

    private void playDoReMi() {
        // 创建合成器
        Synthesizer synth = JSyn.createSynthesizer();
        synth.getAudioDeviceManager().setSuggestedOutputLatency(0.123);

        // 添加模块
        SineOscillator voice = new SineOscillator();

        LineOut lineOut = new LineOut();
        synth.add(voice);
        synth.add(lineOut);

        // 连接音频模块
        voice.getOutput().connect(0, lineOut.input, 0);
        voice.getOutput().connect(0, lineOut.input, 1);

        // 启动合成器
        synth.start();
        TimeStamp timeStamp = new TimeStamp(synth.getCurrentTime() + 0.5);
        synth.startUnit(lineOut, timeStamp);

        double C4= 261;
        double Bb3 = C4/1.059/1.059 ;

//        double A3 = C4/1.059/1.059/1.059 ;
        List<Double> twelveToneEqualTemperamentList = generateTwelveToneEqualTemperament(Bb3,14);
        int[] majorScaleSteps = { 0, 2, 4, 5, 7, 9, 11, 12}; // 大调音阶的半音位置

        List<Double> twelveToneEqualTemperamentListInBb = new ArrayList<>();
        twelveToneEqualTemperamentListInBb.add((double) 0);
        for(int i:majorScaleSteps){
            twelveToneEqualTemperamentListInBb.add(twelveToneEqualTemperamentList.get(i));
        }


        double quarter = 0.5;
        double volume = 0.6;
        double gap = 0.03;
        List<Note> melody = Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),  // 我
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),  // 想
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),  // 你
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),  // 已
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter/2, volume, 0),  // 表
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter/2, volume, gap),  // 表
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),  // 现
                new Note(twelveToneEqualTemperamentListInBb.get(7)/2, quarter, volume, gap)   // 的
        );

        List<Note> melody2 = Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),  // 我
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),  // 想
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter/2, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter/2, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, 0),  //
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter*2, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5)/2, quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap)   //
        );

        List<Note> melody3 = Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter/2, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter/2, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),  //
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),  //
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),  //
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter*2, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5)/2, quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap)
        );


        List<Note> melody4 = Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),

                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter*2, volume, gap)
        );

        List<Note> preChorus = new ArrayList<>();
        //你说你也会难过
        preChorus.addAll(Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(0), quarter, volume, gap),  // 我
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),  // 想
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),  // 你
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),  // 已
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),  // 表
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter/2, volume, gap),  // 到
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter/2, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter/2, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter/2, volume, gap),  // 你
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap)  // 你
        ));

        //我不相信
        preChorus.addAll(Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),  // 我
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),  // 想
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),  // 你
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),  // 已
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter*4, volume, gap)  // 表
           ));


        //牵着你陪着你我也
        preChorus.addAll(Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(0), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5)/2, quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter/2, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter/2, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap)
        )
        );

        //只是曾经 希
        preChorus.addAll(Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter*1.5, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter*3, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(6)/2, quarter, volume, gap)
        ));


        //望他是真的比我
        preChorus.addAll(Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(7)/2, quarter, volume, gap)
        ));

        //还要爱你我
        preChorus.addAll(Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(7)/2, quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5)/2, quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5)/2, quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(0), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap)
        ));


        //才会逼自己离开
        preChorus.addAll(Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap)
        ));

        List<Note> chorus =  new ArrayList<>();
        //你已经远远离开
        chorus.addAll(Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(0), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5)/2, quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5), quarter, volume, gap)
        ));


        //我也会慢慢走开
        chorus.addAll(Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(0), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5)/2, quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5), quarter, volume, gap)
        ));


        //为什么我连分开
        chorus.addAll( Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(0), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5)/2, quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap)
        ));


        //都要迁就着你
        chorus.addAll(Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter*2, volume, gap)
        ));


        //我真的没用天分
        chorus.addAll(Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(0), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(7)/2, quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap)
        ));


        //安静的没那么快
        chorus.addAll(Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(0), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(5), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(7)/2, quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap)
        ));


        //我会学者放弃你
        chorus.addAll(Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(0), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap)
        ));


        //是因为我太爱你
        chorus.addAll(Arrays.asList(
                new Note(twelveToneEqualTemperamentListInBb.get(0), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(4), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(3), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(2), quarter/2, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter/2, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(6)/2, quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(7)/2, quarter, volume, gap),
                new Note(twelveToneEqualTemperamentListInBb.get(1), quarter, volume, gap)
        ));



        for (Note note : melody) {
            timeStamp = playNote(voice, note.getFrequency(), note.getVolume(), note.getDuration(), note.getGapDuration(), timeStamp);
        }

        for (Note note : melody2) {
            timeStamp = playNote(voice, note.getFrequency(), note.getVolume(), note.getDuration(), note.getGapDuration(), timeStamp);
        }

        for (Note note : melody3) {
            timeStamp = playNote(voice, note.getFrequency(), note.getVolume(), note.getDuration(), note.getGapDuration(), timeStamp);
        }

        for (Note note : melody4) {
            timeStamp = playNote(voice, note.getFrequency(), note.getVolume(), note.getDuration(), note.getGapDuration(), timeStamp);
        }


        for(Note note :preChorus){
            timeStamp = playNote(voice, note.getFrequency(), note.getVolume(), note.getDuration(), note.getGapDuration(), timeStamp);
        }

        for(Note note :chorus){
            timeStamp = playNote(voice, note.getFrequency(), note.getVolume(), note.getDuration(), note.getGapDuration(), timeStamp);
        }

        // 等待播放完成
        try {
            synth.sleepUntil(timeStamp.getTime() + 1.0);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 停止合成器
        synth.stop();
    }

    /**
     * 播放单个音符
     *
     * @param voice      振荡器
     * @param freq       音符频率 (Hz)
     * @param amplitude  音量大小 (0.0 - 1.0)
     * @param duration   音符持续时间 (秒)
     * @param timeStamp  当前时间戳
     * @return 下一个时间戳
     */
    private TimeStamp playNote(SineOscillator voice, double freq, double amplitude, double duration, TimeStamp timeStamp) {
        double gapDuration = 0.1;
        // 播放音符
        voice.noteOn(freq, amplitude, timeStamp);
        // 关闭音符
        voice.noteOff(timeStamp.makeRelative(duration));
        // 返回下一个音符的时间戳
        return timeStamp.makeRelative(duration+gapDuration);
    }

    private static TimeStamp playNote(SineOscillator voice, double frequency, double volume, double duration, double gapDuration, TimeStamp timeStamp) {
        voice.noteOn(frequency, volume, timeStamp);
        voice.noteOff(timeStamp.makeRelative(duration));
        return timeStamp.makeRelative(duration + gapDuration);
    }

    public static void main(String[] args) {
        new PlayNotes().playDoReMi();
    }
}
